// This file is a part of Funky C++.
// Copyright (C) 2011  Valentin David
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
#ifndef _FUNKY_CONVOLUTION_HH
# define _FUNKY_CONVOLUTION_HH

# include "signal.hh"
# include "../quotient.hh"

namespace funky {
  template <typename Signal, typename Sample, size_t N>
  struct convolution {
  private:
    Signal signal;
    bus<Sample, N> feed;
    quotient<size_t, N> position;
    bus<Sample, N> impulse;
  public:
    template <typename S>
    convolution(S&& s, bus<Sample, N>&& i)
      : signal(std::forward<S>(s)),
	feed(), position(0),
	impulse(std::move(i)) {
      feed[position] = *signal;
    }

    convolution(const convolution&) = default;
    convolution(convolution&& other):
      signal(std::move(other.signal)),
      feed(std::move(other.feed)),
      position(other.position),
      impulse(std::move(other.impulse))
    {
    }

    convolution& operator++() {
      ++signal;
      ++position;
      feed[position] = *signal;
      return *this;
    }

    Sample operator*() const {
      Sample ret = feed[position] * impulse[0];
      for (size_t i = 1; i < N; ++i)
	ret += feed[position+i] * impulse[i];
      return ret;
    }
  };

  template <typename Signal, typename Sample, size_t N>
  struct signal_traits<convolution<Signal, Sample, N> > {
    typedef Sample sample;
    typedef size_t duration;
  };

  template <size_t N,
	    typename T, typename U,
	    typename RT = typename std::decay<T>::type,
	    typename Sample = typename signal_traits<U>::sample
	    >
  convolution<RT, Sample, N> convolve(T&& t, U u) {
    bus<Sample, N> impulse;
    for (size_t i = 0; i < N; ++i) {
      impulse[N-i-1] = *u;
      ++u;
    }
    return convolution<RT, Sample, N>{
      std::forward<T>(t),
	std::move(impulse)
	};
  }
}

#endif
